1 package org.apache.maven.surefire.junitcore.pc;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.surefire.testset.TestSetFailedException;
23 import org.junit.runner.Computer;
24 import org.junit.runner.Description;
25
26 import java.util.Collection;
27 import java.util.TreeSet;
28 import java.util.concurrent.Callable;
29 import java.util.concurrent.ExecutionException;
30 import java.util.concurrent.Executors;
31 import java.util.concurrent.Future;
32 import java.util.concurrent.ScheduledExecutorService;
33
34 import static java.util.concurrent.TimeUnit.NANOSECONDS;
35
36
37
38
39
40
41
42
43 public abstract class ParallelComputer
44 extends Computer
45 {
46 private static final double NANOS_IN_A_SECOND = 1E9;
47
48 private final ShutdownStatus shutdownStatus = new ShutdownStatus();
49
50 private final ShutdownStatus forcedShutdownStatus = new ShutdownStatus();
51
52 private final long timeoutNanos;
53
54 private final long timeoutForcedNanos;
55
56 private ScheduledExecutorService shutdownScheduler;
57
58 public ParallelComputer( double timeoutInSeconds, double timeoutForcedInSeconds )
59 {
60 this.timeoutNanos = secondsToNanos( timeoutInSeconds );
61 this.timeoutForcedNanos = secondsToNanos( timeoutForcedInSeconds );
62 }
63
64 protected abstract ShutdownResult describeStopped( boolean shutdownNow );
65
66 abstract boolean shutdownThreadPoolsAwaitingKilled();
67
68 protected final void beforeRunQuietly()
69 {
70 shutdownStatus.setDescriptionsBeforeShutdown( hasTimeout() ? scheduleShutdown() : null );
71 forcedShutdownStatus.setDescriptionsBeforeShutdown( hasTimeoutForced() ? scheduleForcedShutdown() : null );
72 }
73
74 protected final boolean afterRunQuietly()
75 {
76 shutdownStatus.tryFinish();
77 forcedShutdownStatus.tryFinish();
78 boolean notInterrupted = true;
79 if ( shutdownScheduler != null )
80 {
81 shutdownScheduler.shutdownNow();
82
83
84
85
86 Thread.interrupted();
87 try
88 {
89 shutdownScheduler.awaitTermination( Long.MAX_VALUE, NANOSECONDS );
90 }
91 catch ( InterruptedException e )
92 {
93 notInterrupted = false;
94 }
95 }
96 notInterrupted &= shutdownThreadPoolsAwaitingKilled();
97 return notInterrupted;
98 }
99
100 public String describeElapsedTimeout()
101 throws TestSetFailedException
102 {
103 final StringBuilder msg = new StringBuilder();
104 final boolean isShutdownTimeout = shutdownStatus.isTimeoutElapsed();
105 final boolean isForcedShutdownTimeout = forcedShutdownStatus.isTimeoutElapsed();
106 if ( isShutdownTimeout || isForcedShutdownTimeout )
107 {
108 msg.append( "The test run has finished abruptly after timeout of " );
109 msg.append( nanosToSeconds( minTimeout( timeoutNanos, timeoutForcedNanos ) ) );
110 msg.append( " seconds.\n" );
111
112 try
113 {
114 final TreeSet<String> executedTests = new TreeSet<String>();
115 final TreeSet<String> incompleteTests = new TreeSet<String>();
116
117 if ( isShutdownTimeout )
118 {
119 printShutdownHook( executedTests, incompleteTests, shutdownStatus.getDescriptionsBeforeShutdown() );
120 }
121
122 if ( isForcedShutdownTimeout )
123 {
124 printShutdownHook( executedTests, incompleteTests,
125 forcedShutdownStatus.getDescriptionsBeforeShutdown() );
126 }
127
128 if ( !executedTests.isEmpty() )
129 {
130 msg.append( "These tests were executed in prior to the shutdown operation:\n" );
131 for ( String executedTest : executedTests )
132 {
133 msg.append( executedTest ).append( '\n' );
134 }
135 }
136
137 if ( !incompleteTests.isEmpty() )
138 {
139 msg.append( "These tests are incomplete:\n" );
140 for ( String incompleteTest : incompleteTests )
141 {
142 msg.append( incompleteTest ).append( '\n' );
143 }
144 }
145 }
146 catch ( InterruptedException e )
147 {
148 throw new TestSetFailedException( "Timed termination was interrupted.", e );
149 }
150 catch ( ExecutionException e )
151 {
152 throw new TestSetFailedException( e.getLocalizedMessage(), e.getCause() );
153 }
154 }
155 return msg.toString();
156 }
157
158 private Future<ShutdownResult> scheduleShutdown()
159 {
160 return getShutdownScheduler().schedule( createShutdownTask(), timeoutNanos, NANOSECONDS );
161 }
162
163 private Future<ShutdownResult> scheduleForcedShutdown()
164 {
165 return getShutdownScheduler().schedule( createForcedShutdownTask(), timeoutForcedNanos, NANOSECONDS );
166 }
167
168 private ScheduledExecutorService getShutdownScheduler()
169 {
170 if ( shutdownScheduler == null )
171 {
172 shutdownScheduler = Executors.newScheduledThreadPool( 2 );
173 }
174 return shutdownScheduler;
175 }
176
177 private Callable<ShutdownResult> createShutdownTask()
178 {
179 return new Callable<ShutdownResult>()
180 {
181 public ShutdownResult call()
182 throws Exception
183 {
184 boolean stampedStatusWithTimeout = ParallelComputer.this.shutdownStatus.tryTimeout();
185 return stampedStatusWithTimeout ? ParallelComputer.this.describeStopped( false ) : null;
186 }
187 };
188 }
189
190 private Callable<ShutdownResult> createForcedShutdownTask()
191 {
192 return new Callable<ShutdownResult>()
193 {
194 public ShutdownResult call()
195 throws Exception
196 {
197 boolean stampedStatusWithTimeout = ParallelComputer.this.forcedShutdownStatus.tryTimeout();
198 return stampedStatusWithTimeout ? ParallelComputer.this.describeStopped( true ) : null;
199 }
200 };
201 }
202
203 private double nanosToSeconds( long nanos )
204 {
205 return (double) nanos / NANOS_IN_A_SECOND;
206 }
207
208 private boolean hasTimeout()
209 {
210 return timeoutNanos > 0;
211 }
212
213 private boolean hasTimeoutForced()
214 {
215 return timeoutForcedNanos > 0;
216 }
217
218 private static long secondsToNanos( double seconds )
219 {
220 double nanos = seconds > 0 ? seconds * NANOS_IN_A_SECOND : 0;
221 return Double.isInfinite( nanos ) || nanos >= Long.MAX_VALUE ? 0 : (long) nanos;
222 }
223
224 private static long minTimeout( long timeout1, long timeout2 )
225 {
226 if ( timeout1 == 0 )
227 {
228 return timeout2;
229 }
230 else if ( timeout2 == 0 )
231 {
232 return timeout1;
233 }
234 else
235 {
236 return Math.min( timeout1, timeout2 );
237 }
238 }
239
240 private static void printShutdownHook( Collection<String> executedTests, Collection<String> incompleteTests,
241 Future<ShutdownResult> testsBeforeShutdown )
242 throws ExecutionException, InterruptedException
243 {
244 if ( testsBeforeShutdown != null )
245 {
246 for ( final Description test : testsBeforeShutdown.get().getTriggeredTests() )
247 {
248 if ( test != null && test.getDisplayName() != null )
249 {
250 executedTests.add( test.getDisplayName() );
251 }
252 }
253
254 for ( final Description test : testsBeforeShutdown.get().getIncompleteTests() )
255 {
256 if ( test != null && test.getDisplayName() != null )
257 {
258 incompleteTests.add( test.getDisplayName() );
259 }
260 }
261 }
262 }
263 }